home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / tcsh / dist / sh.file.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-21  |  19.4 KB  |  816 lines

  1. /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.01/RCS/sh.file.c,v 3.3 1991/12/19 22:34:14 christos Exp $ */
  2. /*
  3.  * sh.file.c: File completion for csh. This file is not used in tcsh.
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #ifdef FILEC
  38. #include "sh.h"
  39.  
  40. RCSID("$Id: sh.file.c,v 3.3 1991/12/19 22:34:14 christos Exp $")
  41.  
  42. /*
  43.  * Tenex style file name recognition, .. and more.
  44.  * History:
  45.  *    Author: Ken Greer, Sept. 1975, CMU.
  46.  *    Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  47.  */
  48.  
  49. #define ON    1
  50. #define OFF    0
  51. #ifndef TRUE
  52. #define TRUE 1
  53. #endif
  54. #ifndef FALSE
  55. #define FALSE 0
  56. #endif
  57.  
  58. #define ESC    '\033'
  59.  
  60. typedef enum {
  61.     LIST, RECOGNIZE
  62. }       COMMAND;
  63.  
  64. static    void     setup_tty        __P((int));
  65. static    void     back_to_col_1        __P((void));
  66. static    void     pushback        __P((Char *));
  67. static    void     catn            __P((Char *, Char *, int));
  68. static    void     copyn            __P((Char *, Char *, int));
  69. static    Char     filetype        __P((Char *, Char *));
  70. static    void     print_by_column    __P((Char *, Char *[], int));
  71. static    Char     *tilde            __P((Char *, Char *));
  72. static    void     retype            __P((void));
  73. static    void     beep            __P((void));
  74. static    void      print_recognized_stuff    __P((Char *));
  75. static    void     extract_dir_and_name    __P((Char *, Char *, Char *));
  76. static    Char    *getentry        __P((DIR *, int));
  77. static    void     free_items        __P((Char **));
  78. static    int     tsearch        __P((Char *, COMMAND, int));
  79. static    int     recognize        __P((Char *, Char *, int, int));
  80. static    int     is_prefix        __P((Char *, Char *));
  81. static    int     is_suffix        __P((Char *, Char *));
  82. static    int     ignored        __P((Char *));
  83.  
  84.  
  85. /*
  86.  * Put this here so the binary can be patched with adb to enable file
  87.  * completion by default.  Filec controls completion, nobeep controls
  88.  * ringing the terminal bell on incomplete expansions.
  89.  */
  90. bool    filec = 0;
  91.  
  92. static void
  93. setup_tty(on)
  94.     int     on;
  95. {
  96. #ifdef TERMIO
  97. # ifdef POSIX
  98.     static struct termios tchars;
  99. # else
  100.     static struct termio tchars;
  101. # endif /* POSIX */
  102.  
  103.     if (on) {
  104. # ifdef POSIX
  105.     (void) tcgetattr(SHIN, &tchars);
  106. # else
  107.         (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars);
  108. # endif /* POSIX */
  109.     tchars.c_cc[VEOL] = ESC;
  110.     if (tchars.c_lflag & ICANON)
  111. # ifdef POSIX
  112.         on = TCSANOW;
  113. # else
  114.         on = TCSETAW;
  115. # endif /* POSIX */
  116.     else {
  117. # ifdef POSIX
  118.         on = TCSAFLUSH;
  119. # else
  120.         on = TCSETAF;
  121. # endif /* POSIX */
  122.         tchars.c_lflag |= ICANON;
  123.     
  124.     }
  125. #ifdef POSIX
  126.         (void) tcsetattr(SHIN, on, &tchars);
  127. #else
  128.         (void) ioctl(SHIN, on, (ioctl_t) &tchars);
  129. #endif /* POSIX */
  130.     }
  131.     else {
  132.     tchars.c_cc[VEOL] = _POSIX_VDISABLE;
  133. # ifdef POSIX
  134.     (void) tcsetattr(SHIN, TCSANOW, &tchars);
  135. # else
  136.         (void) ioctl(SHIN, TCSETAW, (ioctl_t) &tchars);
  137. # endif /* POSIX */
  138.     }
  139. #else
  140.     struct sgttyb sgtty;
  141.     static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */
  142.  
  143.     if (on) {
  144.     (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars);
  145.     tchars.t_brkc = ESC;
  146.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  147.     /*
  148.      * This must be done after every command: if the tty gets into raw or
  149.      * cbreak mode the user can't even type 'reset'.
  150.      */
  151.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty);
  152.     if (sgtty.sg_flags & (RAW | CBREAK)) {
  153.         sgtty.sg_flags &= ~(RAW | CBREAK);
  154.         (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty);
  155.     }
  156.     }
  157.     else {
  158.     tchars.t_brkc = -1;
  159.     (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  160.     }
  161. #endif /* TERMIO */
  162. }
  163.  
  164. /*
  165.  * Move back to beginning of current line
  166.  */
  167. static void
  168. back_to_col_1()
  169. {
  170. #ifdef TERMIO
  171. # ifdef POSIX
  172.     struct termios tty, tty_normal;
  173. # else
  174.     struct termio tty, tty_normal;
  175. # endif /* POSIX */
  176. #else
  177.     struct sgttyb tty, tty_normal;
  178. #endif /* TERMIO */
  179.  
  180. # ifdef BSDSIGS
  181.     sigmask_t omask = sigblock(sigmask(SIGINT));
  182. # else
  183.     sighold(SIGINT);
  184. # endif /* BSDSIGS */
  185.  
  186. #ifdef TERMIO
  187. # ifdef POSIX
  188.     (void) tcgetattr(SHOUT, &tty);
  189. # else
  190.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal);
  191. # endif /* POSIX */
  192.     tty_normal = tty;
  193.     tty.c_iflag &= ~INLCR;
  194.     tty.c_oflag &= ~ONLCR;
  195. # ifdef POSIX
  196.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  197. # else
  198.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  199. # endif /* POSIX */
  200.     (void) write(SHOUT, "\r", 1);
  201. # ifdef POSIX
  202.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  203. # else
  204.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  205. # endif /* POSIX */
  206. #else
  207.     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty);
  208.     tty_normal = tty;
  209.     tty.sg_flags &= ~CRMOD;
  210.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty);
  211.     (void) write(SHOUT, "\r", 1);
  212.     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal);
  213. #endif /* TERMIO */
  214.  
  215. # ifdef BSDSIGS
  216.     (void) sigsetmask(omask);
  217. # else
  218.     (void) sigrelse(SIGINT);
  219. # endif /* BSDISGS */
  220. }
  221.  
  222. /*
  223.  * Push string contents back into tty queue
  224.  */
  225. static void
  226. pushback(string)
  227.     Char   *string;
  228. {
  229.     register Char *p;
  230.     char    c;
  231. #ifdef TERMIO
  232. # ifdef POSIX
  233.     struct termios tty, tty_normal;
  234. # else
  235.     struct termio tty, tty_normal;
  236. # endif /* POSIX */
  237. #else
  238.     struct sgttyb tty, tty_normal;
  239. #endif /* TERMIO */
  240.  
  241. #ifdef BSDSIGS
  242.     sigmask_t omask = sigblock(sigmask(SIGINT));
  243. #else
  244.     sighold(SIGINT);
  245. #endif /* BSDSIGS */
  246.  
  247. #ifdef TERMIO
  248. # ifdef POSIX
  249.     (void) tcgetattr(SHOUT, &tty);
  250. # else
  251.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  252. # endif /* POSIX */
  253.     tty_normal = tty;
  254.     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
  255. # ifdef POSIX
  256.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  257. # else
  258.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  259. # endif /* POSIX */
  260.  
  261.     for (p = string; c = *p; p++)
  262.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  263. # ifdef POSIX
  264.     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
  265. # else
  266.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  267. # endif /* POSIX */
  268.     (void) sigsetmask(omask);
  269. #else
  270.     (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty);
  271.     tty_normal = tty;
  272.     tty.sg_flags &= ~ECHO;
  273.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty);
  274.  
  275.     for (p = string; c = *p; p++)
  276.     (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  277.     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal);
  278. #endif /* TERMIO */
  279.  
  280. # ifdef BSDSIGS
  281.     (void) sigsetmask(omask);
  282. # else
  283.     (void) sigrelse(SIGINT);
  284. # endif /* BSDISGS */
  285. }
  286.  
  287. /*
  288.  * Concatenate src onto tail of des.
  289.  * Des is a string whose maximum length is count.
  290.  * Always null terminate.
  291.  */
  292. static void
  293. catn(des, src, count)
  294.     register Char *des, *src;
  295.     register count;
  296. {
  297.     while (--count >= 0 && *des)
  298.     des++;
  299.     while (--count >= 0)
  300.     if ((*des++ = *src++) == 0)
  301.         return;
  302.     *des = '\0';
  303. }
  304.  
  305. /*
  306.  * Like strncpy but always leave room for trailing \0
  307.  * and always null terminate.
  308.  */
  309. static void
  310. copyn(des, src, count)
  311.     register Char *des, *src;
  312.     register count;
  313. {
  314.     while (--count >= 0)
  315.     if ((*des++ = *src++) == 0)
  316.         return;
  317.     *des = '\0';
  318. }
  319.  
  320. static  Char
  321. filetype(dir, file)
  322.     Char   *dir, *file;
  323. {
  324.     Char    path[MAXPATHLEN];
  325.     struct stat statb;
  326.  
  327.     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
  328.     if (lstat(short2str(path), &statb) == 0) {
  329.     switch (statb.st_mode & S_IFMT) {
  330.     case S_IFDIR:
  331.         return ('/');
  332.  
  333.     case S_IFLNK:
  334.         if (stat(short2str(path), &statb) == 0 &&    /* follow it out */
  335.         S_ISDIR(statb.st_mode))
  336.         return ('>');
  337.         else
  338.         return ('@');
  339.  
  340.     case S_IFSOCK:
  341.         return ('=');
  342.  
  343.     default:
  344.         if (statb.st_mode & 0111)
  345.         return ('*');
  346.     }
  347.     }
  348.     return (' ');
  349. }
  350.  
  351. static struct winsize win;
  352.  
  353. /*
  354.  * Print sorted down columns
  355.  */
  356. static void
  357. print_by_column(dir, items, count)
  358.     Char   *dir, *items[];
  359.     int     count;
  360. {
  361.     register int i, rows, r, c, maxwidth = 0, columns;
  362.  
  363.     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
  364.     win.ws_col = 80;
  365.     for (i = 0; i < count; i++)
  366.     maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
  367.     maxwidth += 2;        /* for the file tag and space */
  368.     columns = win.ws_col / maxwidth;
  369.     if (columns == 0)
  370.     columns = 1;
  371.     rows = (count + (columns - 1)) / columns;
  372.     for (r = 0; r < rows; r++) {
  373.     for (c = 0; c < columns; c++) {
  374.         i = c * rows + r;
  375.         if (i < count) {
  376.         register int w;
  377.  
  378.         xprintf("%s", short2str(items[i]));
  379.         xputchar(dir ? filetype(dir, items[i]) : ' ');
  380.         if (c < columns - 1) {    /* last column? */
  381.             w = Strlen(items[i]) + 1;
  382.             for (; w < maxwidth; w++)
  383.             xputchar(' ');
  384.         }
  385.         }
  386.     }
  387.     xputchar('\r');
  388.     xputchar('\n');
  389.     }
  390. }
  391.  
  392. /*
  393.  * Expand file name with possible tilde usage
  394.  *    ~person/mumble
  395.  * expands to
  396.  *    home_directory_of_person/mumble
  397.  */
  398. static Char *
  399. tilde(new, old)
  400.     Char   *new, *old;
  401. {
  402.     register Char *o, *p;
  403.     register struct passwd *pw;
  404.     static Char person[40];
  405.  
  406.     if (old[0] != '~')
  407.     return (Strcpy(new, old));
  408.  
  409.     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
  410.     *p = '\0';
  411.     if (person[0] == '\0')
  412.     (void) Strcpy(new, value(STRhome));
  413.     else {
  414.     pw = getpwnam(short2str(person));
  415.     if (pw == NULL)
  416.         return (NULL);
  417.     (void) Strcpy(new, str2short(pw->pw_dir));
  418.     }
  419.     (void) Strcat(new, o);
  420.     return (new);
  421. }
  422.  
  423. /*
  424.  * Cause pending line to be printed
  425.  */
  426. static void
  427. retype()
  428. {
  429. #ifdef TERMIO
  430. # ifdef POSIX
  431.     struct termios tty;
  432.  
  433.     (void) tcgetattr(SHOUT, &tty);
  434. # else
  435.     struct termio tty;
  436.  
  437.     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty);
  438. # endif /* POSIX */
  439.  
  440.     tty.c_lflag |= PENDIN;
  441.  
  442. # ifdef POSIX
  443.     (void) tcsetattr(SHOUT, TCSANOW, &tty);
  444. # else
  445.     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  446. # endif /* POSIX */
  447. #else
  448.     int     pending_input = LPENDIN;
  449.  
  450.     (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input);
  451. #endif /* TERMIO */
  452. }
  453.  
  454. static void
  455. beep()
  456. {
  457.     if (adrof(STRnobeep) == 0)
  458.     (void) write(SHOUT, "\007", 1);
  459. }
  460.  
  461. /*
  462.  * Erase that silly ^[ and
  463.  * print the recognized part of the string
  464.  */
  465. static void
  466. print_recognized_stuff(recognized_part)
  467.     Char   *recognized_part;
  468. {
  469.     /* An optimized erasing of that silly ^[ */
  470.     putraw('\b');
  471.     putraw('\b');
  472.     switch (Strlen(recognized_part)) {
  473.  
  474.     case 0:            /* erase two Characters: ^[ */
  475.     putraw(' ');
  476.     putraw(' ');
  477.     putraw('\b');
  478.     putraw('\b');
  479.     break;
  480.  
  481.     case 1:            /* overstrike the ^, erase the [ */
  482.     xprintf("%s", short2str(recognized_part));
  483.     putraw(' ');
  484.     putraw('\b');
  485.     break;
  486.  
  487.     default:            /* overstrike both Characters ^[ */
  488.     xprintf("%s", short2str(recognized_part));
  489.     break;
  490.     }
  491.     flush();
  492. }
  493.  
  494. /*
  495.  * Parse full path in file into 2 parts: directory and file names
  496.  * Should leave final slash (/) at end of dir.
  497.  */
  498. static void
  499. extract_dir_and_name(path, dir, name)
  500.     Char   *path, *dir, *name;
  501. {
  502.     register Char *p;
  503.  
  504.     p = Strrchr(path, '/');
  505.     if (p == NULL) {
  506.     copyn(name, path, MAXNAMLEN);
  507.     dir[0] = '\0';
  508.     }
  509.     else {
  510.     copyn(name, ++p, MAXNAMLEN);
  511.     copyn(dir, path, p - path);
  512.     }
  513. }
  514.  
  515. static Char *
  516. getentry(dir_fd, looking_for_lognames)
  517.     DIR    *dir_fd;
  518.     int     looking_for_lognames;
  519. {
  520.     register struct passwd *pw;
  521.     register struct dirent *dirp;
  522.  
  523.     if (looking_for_lognames) {
  524.     if ((pw = getpwent()) == NULL)
  525.         return (NULL);
  526.     return (str2short(pw->pw_name));
  527.     }
  528.     if (dirp = readdir(dir_fd))
  529.     return (str2short(dirp->d_name));
  530.     return (NULL);
  531. }
  532.  
  533. static void
  534. free_items(items)
  535.     register Char **items;
  536. {
  537.     register int i;
  538.  
  539.     for (i = 0; items[i]; i++)
  540.     xfree((ptr_t) items[i]);
  541.     xfree((ptr_t) items);
  542. }
  543.  
  544. #ifdef BSDSIGS
  545. # define FREE_ITEMS(items) { \
  546.     sigmask_t omask;\
  547. \
  548.     omask = sigblock(sigmask(SIGINT));\
  549.     free_items(items);\
  550.     items = NULL;\
  551.     (void) sigsetmask(omask);\
  552. }
  553. #else
  554. # define FREE_ITEMS(items) { \
  555.     (void) sighold(SIGINT);\
  556.     free_items(items);\
  557.     items = NULL;\
  558.     (void) sigrelse(SIGINT);\
  559. }
  560. #endif /* BSDSIGS */
  561.  
  562. /*
  563.  * Perform a RECOGNIZE or LIST command on string "word".
  564.  */
  565. static int
  566. tsearch(word, command, max_word_length)
  567.     Char   *word;
  568.     int     max_word_length;
  569.     COMMAND command;
  570. {
  571.     static Char **items = NULL;
  572.     register DIR *dir_fd;
  573.     register numitems = 0, ignoring = TRUE, nignored = 0;
  574.     register name_length, looking_for_lognames;
  575.     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
  576.     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
  577.     Char   *entry;
  578.  
  579. #define MAXITEMS 1024
  580.  
  581.     if (items != NULL)
  582.     FREE_ITEMS(items);
  583.  
  584.     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  585.     if (looking_for_lognames) {
  586.     (void) setpwent();
  587.     copyn(name, &word[1], MAXNAMLEN);    /* name sans ~ */
  588.     dir_fd = NULL;
  589.     }
  590.     else {
  591.     extract_dir_and_name(word, dir, name);
  592.     if (tilde(tilded_dir, dir) == 0)
  593.         return (0);
  594.     dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
  595.     if (dir_fd == NULL)
  596.         return (0);
  597.     }
  598.  
  599. again:                /* search for matches */
  600.     name_length = Strlen(name);
  601.     for (numitems = 0; entry = getentry(dir_fd, looking_for_lognames);) {
  602.     if (!is_prefix(name, entry))
  603.         continue;
  604.     /* Don't match . files on null prefix match */
  605.     if (name_length == 0 && entry[0] == '.' &&
  606.         !looking_for_lognames)
  607.         continue;
  608.     if (command == LIST) {
  609.         if (numitems >= MAXITEMS) {
  610.         xprintf("\nYikes!! Too many %s!!\n",
  611.             looking_for_lognames ?
  612.             "names in password file" : "files");
  613.         break;
  614.         }
  615.         if (items == NULL)
  616.         items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
  617.         items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
  618.                            sizeof(Char));
  619.         copyn(items[numitems], entry, MAXNAMLEN);
  620.         numitems++;
  621.     }
  622.     else {            /* RECOGNIZE command */
  623.         if (ignoring && ignored(entry))
  624.         nignored++;
  625.         else if (recognize(extended_name,
  626.                    entry, name_length, ++numitems))
  627.         break;
  628.     }
  629.     }
  630.     if (ignoring && numitems == 0 && nignored > 0) {
  631.     ignoring = FALSE;
  632.     nignored = 0;
  633.     if (looking_for_lognames)
  634.         (void) setpwent();
  635.     else
  636.         rewinddir(dir_fd);
  637.     goto again;
  638.     }
  639.  
  640.     if (looking_for_lognames)
  641.     (void) endpwent();
  642.     else
  643.     (void) closedir(dir_fd);
  644.     if (numitems == 0)
  645.     return (0);
  646.     if (command == RECOGNIZE) {
  647.     if (looking_for_lognames)
  648.         copyn(word, STRtilde, 1);
  649.     else
  650.         /* put back dir part */
  651.         copyn(word, dir, max_word_length);
  652.     /* add extended name */
  653.     catn(word, extended_name, max_word_length);
  654.     return (numitems);
  655.     }
  656.     else {            /* LIST */
  657.     qsort((ptr_t) items, numitems, sizeof(items[0]), sortscmp);
  658.     print_by_column(looking_for_lognames ? NULL : tilded_dir,
  659.             items, numitems);
  660.     if (items != NULL)
  661.         FREE_ITEMS(items);
  662.     }
  663.     return (0);
  664. }
  665.  
  666. /*
  667.  * Object: extend what user typed up to an ambiguity.
  668.  * Algorithm:
  669.  * On first match, copy full entry (assume it'll be the only match)
  670.  * On subsequent matches, shorten extended_name to the first
  671.  * Character mismatch between extended_name and entry.
  672.  * If we shorten it back to the prefix length, stop searching.
  673.  */
  674. static int
  675. recognize(extended_name, entry, name_length, numitems)
  676.     Char   *extended_name, *entry;
  677.     int     name_length, numitems;
  678. {
  679.     if (numitems == 1)        /* 1st match */
  680.     copyn(extended_name, entry, MAXNAMLEN);
  681.     else {            /* 2nd & subsequent matches */
  682.     register Char *x, *ent;
  683.     register int len = 0;
  684.  
  685.     x = extended_name;
  686.     for (ent = entry; *x && *x == *ent++; x++, len++);
  687.     *x = '\0';        /* Shorten at 1st Char diff */
  688.     if (len == name_length)    /* Ambiguous to prefix? */
  689.         return (-1);    /* So stop now and save time */
  690.     }
  691.     return (0);
  692. }
  693.  
  694. /*
  695.  * Return true if check matches initial Chars in template.
  696.  * This differs from PWB imatch in that if check is null
  697.  * it matches anything.
  698.  */
  699. static int
  700. is_prefix(check, template)
  701.     register Char *check, *template;
  702. {
  703.     do
  704.     if (*check == 0)
  705.         return (TRUE);
  706.     while (*check++ == *template++);
  707.     return (FALSE);
  708. }
  709.  
  710. /*
  711.  *  Return true if the Chars in template appear at the
  712.  *  end of check, I.e., are it's suffix.
  713.  */
  714. static int
  715. is_suffix(check, template)
  716.     Char   *check, *template;
  717. {
  718.     register Char *c, *t;
  719.  
  720.     for (c = check; *c++;);
  721.     for (t = template; *t++;);
  722.     for (;;) {
  723.     if (t == template)
  724.         return 1;
  725.     if (c == check || *--t != *--c)
  726.         return 0;
  727.     }
  728. }
  729.  
  730. int
  731. tenex(inputline, inputline_size)
  732.     Char   *inputline;
  733.     int     inputline_size;
  734. {
  735.     register int numitems, num_read;
  736.     char    tinputline[BUFSIZE];
  737.  
  738.  
  739.     setup_tty(ON);
  740.  
  741.     while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
  742.     int     i;
  743.     static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
  744.     '>', '(', ')', '|', '^', '%', '\0'};
  745.     register Char *str_end, *word_start, last_Char, should_retype;
  746.     register int space_left;
  747.     COMMAND command;
  748.  
  749.     for (i = 0; i < num_read; i++)
  750.         inputline[i] = (unsigned char) tinputline[i];
  751.     last_Char = inputline[num_read - 1] & ASCII;
  752.  
  753.     if (last_Char == '\n' || num_read == inputline_size)
  754.         break;
  755.     command = (last_Char == ESC) ? RECOGNIZE : LIST;
  756.     if (command == LIST)
  757.         xputchar('\n');
  758.     str_end = &inputline[num_read];
  759.     if (last_Char == ESC)
  760.         --str_end;        /* wipeout trailing cmd Char */
  761.     *str_end = '\0';
  762.     /*
  763.      * Find LAST occurence of a delimiter in the inputline. The word start
  764.      * is one Character past it.
  765.      */
  766.     for (word_start = str_end; word_start > inputline; --word_start)
  767.         if (Strchr(delims, word_start[-1]))
  768.         break;
  769.     space_left = inputline_size - (word_start - inputline) - 1;
  770.     numitems = tsearch(word_start, command, space_left);
  771.  
  772.     if (command == RECOGNIZE) {
  773.         /* print from str_end on */
  774.         print_recognized_stuff(str_end);
  775.         if (numitems != 1)    /* Beep = No match/ambiguous */
  776.         beep();
  777.     }
  778.  
  779.     /*
  780.      * Tabs in the input line cause trouble after a pushback. tty driver
  781.      * won't backspace over them because column positions are now
  782.      * incorrect. This is solved by retyping over current line.
  783.      */
  784.     should_retype = FALSE;
  785.     if (Strchr(inputline, '\t')) {    /* tab Char in input line? */
  786.         back_to_col_1();
  787.         should_retype = TRUE;
  788.     }
  789.     if (command == LIST)    /* Always retype after a LIST */
  790.         should_retype = TRUE;
  791.     if (should_retype)
  792.         printprompt();
  793.     pushback(inputline);
  794.     if (should_retype)
  795.         retype();
  796.     }
  797.     setup_tty(OFF);
  798.     return (num_read);
  799. }
  800.  
  801. static int
  802. ignored(entry)
  803.     register Char *entry;
  804. {
  805.     struct varent *vp;
  806.     register Char **cp;
  807.  
  808.     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  809.     return (FALSE);
  810.     for (; *cp != NULL; cp++)
  811.     if (is_suffix(entry, *cp))
  812.         return (TRUE);
  813.     return (FALSE);
  814. }
  815. #endif    /* FILEC */
  816.